/*
* Copyright (C) 2012-2016 Julien Bonjean <julien@bonjean.info>
*
* This file is part of Beluga Player.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package info.bonjean.beluga.log;
import info.bonjean.beluga.gui.pivot.ThreadPools;
import info.bonjean.beluga.util.ResourcesUtil;
import java.io.Serializable;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.util.Booleans;
import org.apache.pivot.util.Resources;
import org.apache.pivot.wtk.ApplicationContext;
import org.apache.pivot.wtk.Label;
/**
*
* @author Julien Bonjean <julien@bonjean.info>
*
*/
@Plugin(name = "StatusBar", category = "Core", elementType = "appender", printObject = true)
public final class StatusBarAppender extends AbstractAppender
{
private static final long LOG_MESSAGE_DURATION = 3 * 1000;
private static Label statusBarText;
private static Resources resources;
private LogEvent messageDisplayed;
private ScheduledFuture<?> expirationTaskFuture;
private static StatusBarAppender instance;
protected StatusBarAppender(String name, Filter filter, Layout<? extends Serializable> layout,
boolean ignoreExceptions)
{
super(name, filter, layout, ignoreExceptions);
instance = this;
}
@PluginFactory
public static StatusBarAppender createAppender(@PluginAttribute("name") final String name,
@PluginElement("Layout") Layout<? extends Serializable> layout,
@PluginElement("Filter") Filter filter,
@PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) final String ignore)
{
if (name == null)
{
LOGGER.error("No name provided for StatusBarAppender");
return null;
}
if (layout == null)
{
layout = PatternLayout.createDefaultLayout();
}
final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
return new StatusBarAppender(name, filter, layout, ignoreExceptions);
}
@Override
public void append(LogEvent event)
{
// stop if we didn't receive the UI information
if (statusBarText == null || resources == null)
return;
display(event);
}
private void clearMessage()
{
// clear the status bar
ApplicationContext.queueCallback(new Runnable()
{
@Override
public void run()
{
statusBarText.setText("");
}
}, true);
messageDisplayed = null;
}
public static void clearErrorMessage()
{
if (instance == null)
return;
if (instance.messageDisplayed == null)
return;
if (instance.messageDisplayed.getLevel().isMoreSpecificThan(Level.ERROR))
instance.clearMessage();
}
private final Runnable expirationTask = new Runnable()
{
@Override
public void run()
{
// ensure we don't clear an error message
if (!instance.messageDisplayed.getLevel().isMoreSpecificThan(Level.ERROR))
clearMessage();
}
};
private String formatMessage(LogEvent event)
{
// retrieve the original message
String key = event.getMessage().getFormattedMessage();
// if no key, something bad happened
if (key == null)
key = "unknownMessage";
// try to translate it
String message = (String) resources.get(key);
// if the translation failed, use the original message
if (message == null)
message = key;
// return the shorten the message
return ResourcesUtil.shorten(message, 80);
}
public boolean display(LogEvent event)
{
// if no message currently displayed
if (messageDisplayed == null)
{
doDisplay(event);
return true;
}
// if this is at least as important as what we currently display
if (event.getLevel().isMoreSpecificThan(messageDisplayed.getLevel()))
{
doDisplay(event);
return true;
}
// the message is discarded
return false;
}
public void doDisplay(final LogEvent event)
{
if (!event.getLevel().isMoreSpecificThan(Level.ERROR))
scheduleMessageExpiration();
else
unscheduleMessageExpiration();
messageDisplayed = event;
ApplicationContext.queueCallback(new Runnable()
{
@Override
public void run()
{
String message = formatMessage(event);
if (message != null)
{
statusBarText.setText(message);
if (event.getLevel().isMoreSpecificThan(Level.ERROR))
statusBarText.getStyles().put("color", "#ff0000");
else
statusBarText.getStyles().put("color", "#000000");
}
}
}, false);
}
public void unscheduleMessageExpiration()
{
if (expirationTaskFuture != null && !expirationTaskFuture.isDone())
expirationTaskFuture.cancel(true);
}
public void scheduleMessageExpiration()
{
unscheduleMessageExpiration();
// schedule the expiration task
expirationTaskFuture = ThreadPools.statusBarScheduler.schedule(expirationTask,
LOG_MESSAGE_DURATION, TimeUnit.MILLISECONDS);
}
public static void setLabel(Label statusBarText)
{
StatusBarAppender.statusBarText = statusBarText;
}
public static void setResources(Resources resources)
{
StatusBarAppender.resources = resources;
}
}